library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity memory is
    generic (width: integer := 32; tpd: time := 1 ns);
    port (
        d_bus_out: out std_logic_vector(width-1 downto 0);
        d_bus_in : in  std_logic_vector(width-1 downto 0);
        a_bus    : in  std_logic_vector(width-1 downto 0);
        read     : in  std_ulogic;
        write    : in  std_ulogic;
        ready    : out std_ulogic
        
    );
end memory;

architecture behaviour of memory is
begin
    process
        constant high_address: natural := 300;
        constant unknown: std_logic_vector(width-1 downto 0) := (others => '-');
        
        type mem_array is
            array (natural range 0 to high_address) of std_logic_vector(width-1 downto 0);
        variable mem: mem_array := (
	-- The test cases in this file are exactly the same as the ones in the tests directory.
	-- However, in that directory the files are also available as .asm file, which are more easily readable
	-- Use those files to get a feeling for what exactly is tested. These arrays of zeros and ones can be generated from the .asm files.
	
	-- When testing, the processor will just start from zero and execute everything.
	-- This means it will also try to execute the data segment
	-- Depending on the value of the data this will give an 'illegal instruction detected' warning or it will just try to execute the data
	
	-- blez.asm
	-- "10001100000001110000000001000100", --
	-- "00011000111000000000000000000100", --
	-- "00000000000000000000000000000000", --
	-- "10001100000001110000000001001000", --
	-- "00011000111000000000000000000010", --
	-- "00000000000000000000000000000000", --
	-- "10001100000010000000000001001100", --
	-- "10001100000010010000000001001100", --
	-- "10001100000001110000000001010000", --
	-- "00011000111000000000000000000010", --
	-- "00000000000000000000000000000000", --
	-- "10001100000010100000000001000100", --
	-- "10001100000010110000000001001100", --
	-- "10001100000011000000000001010100", --
	-- "00000000111011000011100000100001", --
	-- "00011000111000001111111111111110", --
	-- "00000000111010010011100000100001", --
	-- "00000000000000000000000000001100", --
	-- "00000000000000000000000000000000", --
	-- "00000000000000000000000000011001", --
	-- "11111111111111111111111111111011", --
	-- "00000000000000000000000000000001", --
	
	-- j.asm
	-- "00001000000000000000000000000011", --
	-- "00000000000000000000000000000000", --
	-- "10001100000001110000000000010000", --
	-- "10001100000010000000000000010100", --
	-- "00000000000000000000000000001010", --
	-- "00000000000000000000000000001111", --
	
	-- lw_sw.asm
	-- "10001100000001110000000000011000", --
	-- "10001100111010000000000000011000", --
	-- "00000000111010000100100000100001", --
	-- "10101100000010000000000000011000", --
	-- "10001100000010100000000000011000", --
	-- "00000001010010100101100000100001", --
	-- "00000000000000000000000000000100", --
	-- "00000000000000001111111011101101", --

	-- nor.asm
	-- "00000000000000000100100000100111", --
	-- "00000001001010010101000000100111", --
	-- "10001100000001110000000000100000", --
	-- "10001100000010000000000000100100", --
	-- "00000000111010000101100000100111", --
	-- "10001100000001110000000000101000", --
	-- "10001100000010000000000000101100", --
	-- "00000000111010000110000000100111", --
	-- "10101010101010101010101010101010", --
	-- "01010101010101010101010101010101", --
	-- "00000000000000001111111011101101", --
	-- "00000000000000001011111011001010", --
	
	-- sll.asm
	-- "10001100000001110000000000100100", --
	-- "00000000000001110100000001000000", --
	-- "00000000000001110100100010000000", --
	-- "00000000000001110101000011000000", --
	-- "00000000000001110101111111000000", --
	-- "00000000000001110110010001000000", --
	-- "00000000000001110110100000000000", --
	-- "00000000000001110111001111000000", --
	-- "00000000000001110111110000000000", --
	-- "00000000111010000000000000011000", --

	-- mult_mfhi_mflo.asm
	-- "10001100000001110000000001000000", --
	-- "10001100000010000000000001000100", --
	-- "10001100000010010000000001001000", --
	-- "10001100000010100000000001001100", --
	-- "00000000111010000000000000011000", --
	-- "00000000000000000101100000010000", --
	-- "00000000000000000110000000010010", --
	-- "00000001000010010000000000011000", --
	-- "00000000000000000110100000010000", --
	-- "00000000000000000111000000010010", --
	-- "00000000111000000000000000011000", --
	-- "00000000000000000111100000010000", --
	-- "00000000000000001000000000010010", --
	-- "00000001000010100000000000011000", --
	-- "00000000000000001000100000010000", --
	-- "00000000000000001001000000010010", --
	-- "00000000111010000000000000011000", --
	-- "00000000000000011101100000100001", --
	-- "11111111111111101011001000001000", --
	-- "10000000000000000000000000000000", --

	-- sltu.asm
	-- "10001100000001110000000000011000", --
	-- "10001100000010000000000000011100", --
	-- "10001100000010010000000000100000", --
	-- "00000000111010000101000000101011", --
	-- "00000000111010010101100000101011", --
	-- "00000000111001110110000000101011", --
	-- "00000000111010000000000000011000", --
	-- "00000000111001001110000111000000", --
	-- "00000000111011001000001011100000", --

	-- add.asm
	-- "10001100000010000000000000100100", -- %% lw		$8,		36($0)
	-- "10001100000010000000000000100100", -- %% lw		$9,		36($0)
	-- "00000001000010010101000000100001", -- %% addu	$10,	$8, 	$9       $9 := 0+0=0
	-- "10001100000010000000000000101000", -- %% lw		$8,		40($0)
	-- "10001100000010010000000000101100", -- %% lw		$9,		44($0)
	-- "00000001000010010100100000100001", -- %% addu	$9,		$8,		$9        $9 := 0xFFFFFFFF+2=0x00000001
	-- "10001100000001110000000000110000", -- %% lw		$7,		48($0)
	-- "10001100000010000000000000110100", -- %% lw		$8,		52($0)
	-- "00000000111010000100100000100001", -- %% addu	$9,		$7,		$8       $9 := 4005+13967=17972
	-- "00000000000000000000000000000000", -- ## Data ## 0
	-- "11111111111111111111111111111111", -- ## Max. integer
	-- "00000000000000000000000000000010", -- ## 2
	-- "00000000000000000000111110100101", -- ## 0x0FA5 (4005)
	-- "00000000000000000011011010001111", -- ## 0x368F (13967)

	-- all_instructions.asm
	"10001100000001110000000001001000", -- # lw		$7,		68($0)
	"10001100000010110000000001000100", -- # lw 		$11,	64($0)
	"00011001011000000000000000000001", -- # blez		$11,	l1
	"10001100000001110000000001010000", -- # lw		$7,		76($0)
	"00000000111001110000000000011000", -- # mult		$7,		$7				# l1
	"00000000000000000100000000010010", -- # mflo		$8
	"00000000000001110100100001000000", -- # sll		$9,		$7,		1
	"00000001000010010101000000100001", -- # addu		$10,	$8,		$9
	"00001000000000000000000000001011", -- # j		l2
	"10001100000010010000000001010100", -- # lw		$9,		80($0)
	"10001100000010000000000001011000", -- # lw		$8,		84($0)
	"00000001011010110110000000100111", -- # nor		$12,	$11,	$11		# l2
	"00000001100011000000000000011000", -- # mult		$12,	$12
	"00000000000000000110100000010000", -- # mfhi		$13
	"00000000000000000111000000010010", -- # mflo		$14
	"10101100000011000000000001011100", -- # sw		$12,	88($0)
	"00000001001010100111100000101011", -- # sltu		$15,	$9,		$10
	"00000000000000000000000000000000", -- ## Data #0
	"00000000000000000000000000000001", -- # 1
	"00000000000000000000000000000011", -- # 3
	"00000000000000000000000000001111", -- # 15
	"00000000000000000000000000010000", -- # 16
	"00000000000000000000000000011001", -- # 25
	
	others => (others => '0')
        );
        
        variable address: natural;
        variable data_out: std_logic_vector(width-1 downto 0);
    begin
        ready <= '0' after tpd;
        wait until (write='1') or (read='1');
        address := to_integer(unsigned(a_bus(31 downto 2)));
        assert (address >= 0) and (address <= high_address) report "out of memory range" severity warning;
        if write='1' then
            mem(address) := d_bus_in;
            ready <= '1' after tpd;
            wait until write='0';
        else
            data_out := mem(address);
            d_bus_out <= data_out;
            ready <= '1' after tpd;
            wait until read='0';
            d_bus_out <= unknown;
        end if;
    end process;
end behaviour;